#!/usr/bin/env python
#
# Parser implementation for SCF2 (recursive-descent parser)
#
# Author:   Mike Murphy (mamurph@cs.clemson.edu)
# Revision: 14 July 2008
#
#   Copyright 2007-2008 Clemson University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# This material is based upon work supported under a National Science
# Foundation Graduate Research Fellowship. Any opinions, findings,
# conclusions or recommendations expressed in this publication are those of
# the author(s) and do not necessarily reflect the views of the National
# Science Foundation.

import sys

from scanner import SCT_TOKEN, SCT_CONTROL, SCT_STRING, SCT_BOOLSTRING, \
      S_LHS, S_RHS
from messages import MSG_SYNTAX_ERROR, MSG_PARSE_ERROR_WITH_LINE, \
      MSG_PARSER_UNEXPECTED_EOF

PTYPE_STRING = 'string'
PTYPE_BOOL = 'bool'
PTYPE_LIST = 'list'
PTYPE_RECORD = 'record'

PT_CONTROL = 'control'
PT_ASSIGN = 'assign'

SCI_TYPE = 0
SCI_STATE = 1
SCI_LINE = 2
SCI_VALUE = 3
SCI_ALT = 4
SCI_FILENAME = 5

def parse_error(token):
   if token != None:
      filename = token[5]
      line = token[2]
      print >> sys.stderr, \
         MSG_PARSE_ERROR_WITH_LINE % (filename, line, MSG_SYNTAX_ERROR)
   else:
      print >> sys.stderr, MSG_PARSER_UNEXPECTED_EOF

# Scanner format: (type, state, line, string, alt, filename)
def match(token, sctype, limit = [], literal = ''):
   matched = True
   if token[0] != sctype and not \
            (token[0] == SCT_STRING and sctype == SCT_BOOLSTRING):
      matched = False
   if len(limit) > 0 and token[1] not in limit:
      matched = False
   if literal != '' and token[3] != literal:
      matched = False
   return matched

class CfgParser(object):
   def __init__(self, scan_ok, scan_results):
      self.ok = scan_ok
      self.tokens = scan_results
      self.index = 0
      self.parse_results = []
      self.stack = [ self.parse_results ]
   def accept(self, pt, tok, ptype, value):
      lhs = tok[SCI_VALUE]
      filename = tok[SCI_FILENAME]
      line = tok[SCI_LINE]
      tup = (pt, lhs, ptype, value, filename, line)
      self.stack[-1].append(tup)
   def chomp(self):
      tok = None
      if self.index < len(self.tokens):
         tok = self.tokens[self.index]
         self.index += 1
      return tok
   def parse(self):
      self.parse_s()
      return (self.ok, self.parse_results)
   def parse_s(self):
      while self.index < len(self.tokens):
         self.parse_e()
   def parse_e(self):
      tok = self.chomp()
      if match(tok, SCT_CONTROL, [S_LHS]):
         tok2 = self.chomp()
         if match(tok2, SCT_STRING, [S_LHS]):
            self.accept(PT_CONTROL, tok, PTYPE_STRING, tok2[SCI_VALUE])
         else:
            parse_error(tok2)
            self.ok = False
      elif match(tok, SCT_STRING, [S_LHS]):
         tok2 = self.chomp()
         if match(tok2, SCT_TOKEN, literal = '='):
            value = self.parse_r()
            if self.ok:
               self.accept(PT_ASSIGN, tok, *value)
         else:
            parse_error(tok2)
            self.ok = False
      else:
         parse_error(tok)
         self.ok = False
   def parse_r(self):
      tok = self.chomp()
      if match(tok, SCT_BOOLSTRING, [S_RHS]):
         value = tok[SCI_VALUE]
         ptype = PTYPE_STRING
         if isinstance(value, bool):
            ptype = PTYPE_BOOL
         return (ptype, value)
      elif match(tok, SCT_TOKEN, [S_RHS], '['):
         return (PTYPE_LIST, self.parse_l())
      elif match(tok, SCT_TOKEN, [S_RHS], '{'):
         return (PTYPE_RECORD, self.parse_t())
      else:
         parse_error(tok)
         self.ok = False
         return ''
   def parse_l(self):
      this_list = []
      tok = self.chomp()
      while not match(tok, SCT_TOKEN, [S_LHS], ']'):
         if tok == None:
            parse_error(None)
         elif match(tok, SCT_TOKEN, literal = '['):
            sublist = self.parse_l()
            this_list.append(sublist)
         else:
            this_list.append(tok[SCI_VALUE])
         tok = self.chomp()
      #
      return this_list
   def parse_t(self):
      subconfig = []
      self.stack.append(subconfig)
      while self.index < len(self.tokens) and \
            not match(self.tokens[self.index], SCT_TOKEN, [S_LHS], '}'):
         self.parse_e()
      closer = self.chomp()
      if closer == None:
         parse_error(None)
         self.ok = False
      return self.stack.pop()

if __name__ == '__main__':
   import scanner
   toss, raw = scanner.read_file(sys.argv[1])
   scanner = scanner.CfgScanner(sys.argv[1], raw)
   ok, results = scanner.scan()
   parser = CfgParser(ok, results)
   pok, pres = parser.parse()
   if pok:
      for result in pres:
         print result